#region File Header
//
// CRC16.cs - Computes a 16-bit CRC 
//
// Copyright (C) Javier Valcarce. BSD License
#endregion

#region Using Statements
using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Text;
#endregion

namespace registra_audio
{
    /// <summary>
    /// Cyclic code redundancy calculator for any 16-bit generator polynomial.
    /// 
    /// Convention: Remaindet initialized with InitValue
    /// Convention: The first serial data bit is the byte's MSB
    /// </summary>
    public class Crc16 
    {
        ushort polinomial;
        ushort crc;
        int    count;

        public const ushort InitValue = 0xFFFF;

        static ushort[] crctable;


        //static void Main(string[] args)
        //{
        //    if (!File.Exists(args[0]))
        //    {
        //        Console.WriteLine("No such file");
        //        return;
        //    }

        //    FileStream ifile = new FileStream(args[0], FileMode.Open);

        //    byte[] b = new byte[ifile.Length];
        //    ifile.Read(b, 0, b.Length);
        //    Crc16 crc16 = new Crc16(0x8005);

        //    Console.WriteLine("Computing CRC-16...");            
        //    for (int i = 0; i < 6; i++)
        //    {
        //        crc16.Compute(0xff);
        //        Console.WriteLine("Feed 0xFF -> 0x{0:X4}", crc16.Redundancy);
        //    }
        //    crc16.Reset();
        //    for (int i = 0; i < 6; i++)
        //    {
        //        crc16.Compute(0x01);
        //        Console.WriteLine("Feed 0x01 -> 0x{0:X4}", crc16.Redundancy);
        //    }
        //    //crc16.Compute(b, 0, b.Length);
        //    //Console.WriteLine("0x{0:X4}", crc16.Redundancy);

        //    Console.WriteLine("{0} bytes computed", crc16.Count);
        //}

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="polinomial">Generator polynomial in hexadecimal notation. For example: the 
        /// polinomial g(x) = 1 + x2 + x15 + x16 has a hexadecimal representation 0x8005</param>
        public Crc16(ushort polinomial)
        {
            Debug.Assert((polinomial & 0x0001) == 0x0001, 
                "Bad polynomial, zero order coeficcient must be 1"); 

            this.polinomial = polinomial;
            // Initialized with InitValue
            crc = InitValue;
            count = 0;

            // table
            crctable = new ushort[0x10000]; // 0xFFFF + 1

            ushort t;
            ushort c;

            for (int r = 0; r < 0x10000; r++)            
            {
                c = (ushort) r;

                for (int j = 0; j < 8; j++)
                {
                    t = (ushort)(c & 0x8000);
                    c = (ushort)(c << 1);

                    if (t == 0x8000)
                    {
                        c = (ushort)(c ^ polinomial);
                    }
                }

                crctable[r] = c;
            }

            //for (int r = 0; r < 0x10000; r++)
            //{
            //    Console.WriteLine("{0,5} -> 0x{1:x4}", r, crctable[r]);
            //}
        }

        #region Sequential Compute CRC
        ///// <summary>
        ///// Compute/Update the CRC with a single byte
        ///// </summary>
        ///// <param name="b"></param>
        //public void Compute(byte b)
        //{
        //    ushort s;
        //    ushort t;

        //    s   = (ushort)(b  << 8);
        //    crc = (ushort)(crc ^ s);

        //    for (int j = 0; j < 8; j++)
        //    {
        //        t   = (ushort)(crc & 0x8000);
        //        crc = (ushort)(crc << 1);

        //        if (t == 0x8000)
        //        {
        //            crc = (ushort)(crc ^ polinomial);
        //        }
        //    }
        //}

        ///// <summary>
        ///// Compute/Update the CRC with an array of bytes
        ///// </summary>
        ///// <param name="buf"></param>
        ///// <param name="offset"></param>
        ///// <param name="count"></param>
        //public void Compute(byte[] buf, int offset, int count)
        //{
        //    ushort s;
        //    ushort t;

        //    for (int i = 0; i < count; i++)
        //    {
        //        s = (ushort)(buf[offset + i] << 8);
        //        crc = (ushort)(crc ^ s);

        //        for (int j = 0; j < 8; j++)
        //        {
        //            t = (ushort)(crc & 0x8000);
        //            crc = (ushort)(crc << 1);

        //            if (t == 0x8000)
        //            {
        //                crc = (ushort)(crc ^ polinomial);
        //            }
        //        }
        //    }

        //    this.count += count;
        //}
        #endregion

        public void ParallelCompute(byte[] buf, int offset, int count)
        {
            ushort s;
           
            for (int i = 0; i < count; i++)
            {
                s   = (ushort)(buf[offset + i] << 8);
                crc = (ushort)(crc ^ s);

                crc = crctable[crc];
            }
            this.count += count;
        }

        public void ParallelCompute(byte b)
        {
            ushort s;

            s   = (ushort)(b  << 8);
            crc = (ushort)(crc ^ s);

            crc = crctable[crc];

            this.count++;
        }

        /// <summary>
        /// Current CRC computed value
        /// </summary>
        public ushort Checksum
        {
            get
            { 
                return crc; 
            }
        }


        /// <summary>
        /// Number of octets processed
        /// </summary>
        public int Count
        {
            get
            {
                return count;
            }
        }

        /// <summary>
        /// Resets current CRC value
        /// </summary>
        public void Reset()
        {
            crc = InitValue;
            count = 0;
        }
       
    }
}
